Digital Filtering - EEG
Difficulty Level:
Tags pre-process☁eeg☁digital filtering

EEG acquired signals are highly vulnerable to different kind of noise.
Some noise can be avoided by preparation of test surroundings, hence other noise can not be avoided and must be removed by specific filtering methods.
The Signal-to-Noise ratio (SNR) of an acquired EEG signal is a measure of signal quality and describes the ratio of signal power to noise power. The raw Electroencephalogram (EEG) data needs filtering in order for proper noise reduction and signal evaluation.
Reasons and implications of artefacts causing the noise are discussed in this Jupyter Notebook as well as the application and implication of different filters on the signal.


1 - Importation of the needed packages

In [1]:
# Biosignalsnotebooks python package
import biosignalsnotebooks as bsnb

# Scientific packages
from numpy import array, mean
from scipy.signal import welch

2 - Loading of acquired EEG data and Unit Conversion
For a detailed explanation on how to load the acquired EEG data as well as performing the Unit Conversion of the raw data, please refer to the notebooks Load acquired data from .txt file and EEG-Unit Conversion

2.1 - Load data from signal samples library

In [2]:
# Load Data from file path
data_artefact_segment_1, header = bsnb.load_signal("eeg_sample_artefacts_seg1", get_header=True)
data_artefact_segment_2 = bsnb.load_signal("eeg_sample_artefacts_seg2")
data_artefact_segment_3 = bsnb.load_signal("eeg_sample_artefacts_seg3")

2.2 - Select the relevant channel to work with

In [3]:
# [The acquired EEG data is at channel 2 ("CH2") 
# for segments 1 and 2 and at channel 1 ("CH1") for segment 3]
eeg_artefact_segment_1 = data_artefact_segment_1["CH2"]
eeg_artefact_segment_2 = data_artefact_segment_2["CH2"]
eeg_artefact_segment_3 = data_artefact_segment_3["CH1"]

2.3 - Store some acquisition constants inside variables
Two extremely relevant parameters defined before data acquisition are the sampling rate and ADC resolution. On the previous cell we only request the header of eeg_sample_artefacts_seg1 signal sample because the remaining signal samples were collected in similar conditions (at the same sampling rate and resolution).

In [4]:
sr = header["sampling rate"] # Sampling Rate
resolution = header["resolution"][0]

2.4 - Conversion of ADC sample values to physical units (uV) and generation of a time-axis
The device used to acquire EEG data belongs to the "biosignalsplux" model. In order to convert the raw EEG signal to its physical units, the recorded data must be passed as an input of raw_to_phy function.

In [5]:
#Unit Conversion
uv_eeg_artefact_segment_1 = bsnb.raw_to_phy("EEG", "biosignalsplux", eeg_artefact_segment_1, resolution, "uV")
time_eeg_artefact_segment_1 = bsnb.generate_time(uv_eeg_artefact_segment_1, sr)

uv_eeg_artefact_segment_2 = bsnb.raw_to_phy("EEG", "biosignalsplux", eeg_artefact_segment_2, resolution, "uV")
time_eeg_artefact_segment_2 = bsnb.generate_time(uv_eeg_artefact_segment_2, sr)

uv_eeg_artefact_segment_3 = bsnb.raw_to_phy("EEG", "biosignalsplux", eeg_artefact_segment_3, resolution, "uV")
time_eeg_artefact_segment_3 = bsnb.generate_time(uv_eeg_artefact_segment_3, sr)

3 - Plot of Raw Data and Artefact check
In the following topics, the previously loaded raw data is compared to check for artefacts influencing the signal.

Types of Artefacts which can influence the signal:

  1. Noise such as coffee machine, elevator, people talking
  2. Movement artefacts such as eye movement, jaw clenching
  3. Electrical Noise such as wlan, electricity (50Hz)
  4. Electrode placement

The latter is explained in detail in the notebook EEG - Electrode Placement
Besides the mentioned artefacts, it needs to be considered that the signal varies among different test subjects!

3.1 Noise Artefacts
Surrounding noise can be eliminated but in some locations not completely be prevented. To illustrate surrounding noise in the signal, the following plot shows a baseline test with closed eyes: The electrodes were placed in the occipital lobe O1 and O2 electrode placement.

In [6]:
#Plot of Raw data - eys closed
bsnb.plot_eeg_signal_wind(time_eeg_artefact_segment_3, uv_eeg_artefact_segment_3, time_range=[0, 90.5], time_windows_evt_1=[(20.5,23), (55, 65),(89.5,90.5)], time_windows_evt_2=[], y_axis_label="Electric Voltage (uV)", legend=["Noise - coffee machine / people talking"])

3.2 - Movement Artefacts:
Movement artefacts such as eye blinking can hardly be inhibited. One can tell the test subject not to blink during a specific time of data acquisition and to relax and focus on a specific point with the eyes prior to the acquisition, though the test subject may execute the mentioned movements unintended.
Hence it is important to know which noise can influence the signal and which parts of the signal are noise.

To illustrate the movement artefacts, the electrodes were placed in the frontal lobe FP1 and FP2 electrode placement.
The following Figures show movement artefacts such as eye and jaw movements:

In [7]:
#Plot of Raw data - eyes closed
bsnb.plot_eeg_signal_wind(time_eeg_artefact_segment_1, uv_eeg_artefact_segment_1, time_range=[35, 55], time_windows_evt_1=[(44,50)],time_windows_evt_2=[], y_axis_label="Electric Voltage (uV)", legend=["Jaw clenching"])
bsnb.plot_eeg_signal_wind(time_eeg_artefact_segment_1, uv_eeg_artefact_segment_1, time_range=[55, 75], time_windows_evt_1=[(63.5,65),(66,67.5)],time_windows_evt_2=[], y_axis_label="Electric Voltage (uV)", legend=["Eye Movement"])

#Plot of Raw data - eyes open
bsnb.plot_eeg_signal_wind(time_eeg_artefact_segment_2, uv_eeg_artefact_segment_2, time_range=[80, 100], time_windows_evt_1=[(88.5,100)], time_windows_evt_2=[], y_axis_label="Electric Voltage (uV)", legend=["Eye blinking"])

Electrical noise such as wireless can be switched off and removed as much as possible prior to data acquisition. Though remaining 50Hz and other noise need to be filtered
After demonstrating how different sources of noise and artefacts can be present on an EEG signal, in the following steps it will be shown how to exclude these undesirable events from our data (through signal processing/digital filtering techniques)

4 - Filtering of the EEG data with a Baseline Shift and different Bandpass Filters
An acquired EEG signal is processed using a 2nd order Bandpass Filter with different cutoff frequencies to eliminate noise and artefacts as well as keeping the frequencies in the range of brain activity of awake adults. Furthermore a baseline shift is performed in order to remove the baseline noise. Please refer to the Notebook Digital Filtering for more information.

4.1 - Load acquired EEG data (collected during an experimental procedure where the volunteer opened and closed his eyes sequentially)

In [8]:
# Load Data from file path
data, header = bsnb.load_signal("eeg_sample_closed_open_eyes", get_header=True)

# [The acquired EEG data is at channel 1 ("CH1")]
eeg_data = data["CH1"]

4.2 - Proceed to the unit conversion and generate a time-axis

In [9]:
#Unit Conversion
signal_uv = bsnb.raw_to_phy("EEG", "biosignalsplux", eeg_data, resolution, "uV")
time_uv = bsnb.generate_time(signal_uv, sr)

4.3 - Definition of the sample numbers where the window under analysis starts and ends

In [10]:
# Time window 
t_start = 0 # lower limit of time window (s)
sample_start = t_start*sr

t_end = 30 # Upper limit of time window (s)
sample_end = t_end*sr

4.4 - Specification of cutoff frequencies that characterise the filtering system and preparation of the signal with baseline shift

A - Wide passband

In [11]:
# Cuttoff frequencies
low_cuttoff_wide = 3 # lower cutoff frequency for bandpass filter (Hz)
high_cuttoff_wide = 30 # Upper cutoff frequency for bandpass filter (Hz)

# Baseline shift of window
signal_shift_window = array(signal_uv[sample_start:sample_end]) - mean(array(signal_uv[sample_start:sample_end]))

B - Narrow passband

In [12]:
#Cutoff frequencies
low_cuttoff_narrow = 8 # lower cutoff frequency for bandpass filter (Hz)
high_cuttoff_narrow = 12 # Upper cutoff frequency for bandpass filter (Hz)

4.5 - Filtering EEG data on the time window under analysis

A - Wide passband

In [13]:
# Digital Bandpass filtering with cutoff frequencies of f1=3 and f2=30 Hz using bsns.bandpass
filtered_signal_3_30 = bsnb.bandpass(signal_shift_window, low_cuttoff_wide, high_cuttoff_wide, order = 2, fs = sr)

B - Narrow passband

In [14]:
# Digital Bandpass filtering with cutoff frequencies of f1=8 and f2=12 Hz using bsns.bandpass
filtered_signal_8_12 = bsnb.bandpass(signal_shift_window, low_cuttoff_narrow, high_cuttoff_narrow, order = 2, fs = sr)

5 - Generation of Power Spectrum by Fast Fourier Transform (FFT)
This step illustrates the influence of difference bandpass filters on the signals such as noise reduction using the power spectrum for illustration.
For more information on FFT please refer to the Notebook Digital Filtering .

In [15]:
#Generate the Power spectrum of unfiltered and filtered signals:
#Power Spectrum of unfiltered Signal
freq_axis, power_spect = bsnb.plotfft(signal_shift_window, sr)

#Power Spectrum of bandpass filtered Signals
freq_axis_3_30, power_spect_3_30 = bsnb.plotfft(filtered_signal_3_30, sr)
freq_axis_8_12, power_spect_8_12 = bsnb.plotfft(filtered_signal_8_12, sr)

The Figure below illustrates the Power spectrum of an acquired EEG signal without applying any bandpass filter. Observe the informational EEG band and make sure to know which frequency bands are containing important information regarding your examination.

In [16]:
#Plot of unfiltered Power Spectrum where the informational band for EEG examination is marked with colour:
fig_1 = bsnb.plot_informational_band(freq_axis, power_spect, signal_shift_window, sr, band_begin=3, band_end=30, legend="EEG Power Spectrum", 
                                     x_lim=[0, 60], y_lim=[0, 25e3], show_plot=True)

In the next two Figures, the power spectrum of the same EEG signal is illustrated applying a bandpass filter with cutoff frequencies of 3 and 30 Hz as well as 8 and 12 Hz.

In [17]:
#Plot of filtered Power Spectra where the informational band for EEG examination is marked with colour:
fig_2 = bsnb.plot([freq_axis_3_30,freq_axis_8_12], [power_spect_3_30,power_spect_8_12], y_axis_label = "Power",x_axis_label = "Frequency (Hz)",title=['Cutoff Freq.:3-30 Hz','Cutoff Freq.:8-12 Hz'], x_range=(0,60), grid_lines=1, grid_columns=2,grid_plot=True)

6- Smoothing of the Power Spectrum unsing Welchs Method
Welchs method . applies the FFT to the filtered signal for specific time windows to estimate the power spectral density.

In [18]:
#Power Spectrum
#Time Windows for Welchs method 
win = 4 * sr
#FFT with time windows using scipy.signal.welch for the 3-30Hz filtered signal
freq_axis_welch, power_spect_welch = welch(filtered_signal_3_30, sr, nperseg=win)
In [19]:
#Plot of Power Spectrum of 3-30Hz bandpass filtered signal where the informational band for EEG examination is marked with colour:
bsnb.plot_informational_band(freq_axis_welch, power_spect_welch, signal_shift_window, sr, band_begin=3, band_end=30, legend="EEG Power Spectrum", 
                             x_lim=[0, 60], y_lim=[0, 20], show_plot=True)
Out[19]:
Figure (
id = '2265', …)

As it can be seen in the Figures above, the 50Hz noise from the unfiltered signal is reduced with the bandpass filter of 3-30 Hz but only completely removed with the bandpass filter of 8-12 Hz, though the latter filter removes the other frequency bands which might be interesting regarding on the specific task and examination. Though using Welchs method applying the FFT on time windows of 4 second each removes the 50 Hz noise with a bandpass filter of 3-30 Hz completely.

Various different artefact types were stated in this notebook, showing that especially in EEG data acquisition one has to be very careful when evaluating data and one need to remember to minimize and remove the mentioned noise from the signal.

We hope that you have enjoyed this guide. biosignalsnotebooks is an environment in continuous expansion, so don"t stop your journey and learn more with the remaining Notebooks !

**Auxiliary Code Segment (should not be replicated by the user)**

In [20]:
from biosignalsnotebooks.__notebook_support__ import css_style_apply
css_style_apply()
.................... CSS Style Applied to Jupyter Notebook .........................
Out[20]: